import java.net.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.*;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class Player extends JFrame implements Runnable, ActionListener, MouseListener
{  
	protected Socket socket              = null;
	protected Thread thread              = null;
	protected DataInputStream  console   = null;
	protected DataOutputStream streamOut = null;
	protected PlayerThread client    = null;
   
  //
	boolean my_turn = false;
    int rows = 16 ;
	int cols = 16 ;
	int numMines = 40 ;
	GridLayout layout = new GridLayout ( rows , cols ) ;
	boolean [ ] mines = new boolean [ rows * cols ] ;
	boolean [ ] clickable = new boolean [ rows * cols ] ;
	boolean lost = false ;
	boolean won = false ;
	boolean firstMove ;
	boolean localPlayEnabled = false ;
	int [ ] numbers = new int [ rows * cols ] ;
	JButton [ ] buttons = new JButton [ rows * cols ] ;
	boolean [ ] clickdone = new boolean [ rows * cols ] ;
	JMenuItem newGameButton = new JMenuItem ( "new game" ) ;
	JLabel mineLabel = new JLabel ( "mines: " + numMines + " marked: 0" ) ;
	JPanel p = new JPanel ( ) ;
	//static Player player = null;
	
	/* Flag Image Resize */
	ImageIcon flag = new ImageIcon("images/flag.png") ;
	Image flagImg = flag.getImage() ;
	Image newFlagImg = flagImg.getScaledInstance( 20, 20,  java.awt.Image.SCALE_SMOOTH ) ;
	ImageIcon newFlag = new ImageIcon( newFlagImg );
	
	/* Mine Image Resize */
	ImageIcon mine = new ImageIcon("images/mine.png") ;
	Image mineImg = mine.getImage() ;
	Image newMineImg = mineImg.getScaledInstance( 20, 20,  java.awt.Image.SCALE_SMOOTH ) ;
	ImageIcon newMine = new ImageIcon( newMineImg );
	
	/* Game Over image */
	ImageIcon gameOverMine = new ImageIcon("images/gameOver.png") ;
	Image gameOverMineImg = gameOverMine.getImage() ;
	Image newGameOverMineImg = gameOverMineImg.getScaledInstance( 25, 25,  java.awt.Image.SCALE_SMOOTH ) ;
	ImageIcon newGameOverMine = new ImageIcon( newGameOverMineImg );
	
	/* Menu Items */
	JButton localPlay = new JButton ( "Local Play" ) ;
	JFrame menu = new JFrame ( "MAIN MENU" ) ;
	JLabel gameTitle = new JLabel ( "Competitive Minesweeper", newMine, 0 ) ;
	JButton networkPlay = new JButton ( "Network Play" ) ;
	JButton quit = new JButton ( "Quit" ) ;
	
	
	private void handle_network_close(){
		JOptionPane.showMessageDialog ( null ,
				"Network Error" , "Error Message" ,
				JOptionPane.ERROR_MESSAGE ) ;
		new MineSweeper();
	}
	
	
	public void gameLayout ( ) {
		p.setLayout ( layout ) ;
		gameSetup ( ) ;
		for ( int i = 0 ; i < ( rows * cols ) ; i ++ ) {
			p.add ( buttons [ i ] ) ;
		}
		this.add ( p ) ;
		this.add ( mineLabel , BorderLayout.SOUTH ) ;
		this.pack () ;
		this.setExtendedState(JFrame.MAXIMIZED_BOTH);
		this.setVisible ( true ) ;
	}
	
	public void fillMines ( ) {
		int needed = numMines ;
		while ( needed > 0 ) {
			int x = ( int ) Math.floor ( Math.random ( ) * rows ) ;
			int y = ( int ) Math.floor ( Math.random ( ) * cols ) ;
			if ( ! mines [ ( rows * y ) + x ] ) {
				mines [ ( rows * y ) + x ] = true ;
				needed -- ;
			}
      else
        mines [ ( rows * y ) + x ] = false ;
		}
	}

	public void fillNumbers ( ) {
		for ( int x = 0 ; x < rows ; x ++ ) {
			for ( int y = 0 ; y < cols ; y ++ ) {
				int cur = ( rows * y ) + x ;
				if ( mines [ cur ] ) {
					numbers [ cur ] = 0 ;
					continue ;
				}
				int temp = 0 ;
				boolean l = ( x - 1 ) >= 0 ;
				boolean r = ( x + 1 ) < rows ;
				boolean u = ( y - 1 ) >= 0 ;
				boolean d = ( y + 1 ) < cols ;
				int left = ( rows * ( y ) ) + ( x - 1 ) ;
				int right = ( rows * ( y ) ) + ( x + 1 ) ;
				int up = ( rows * ( y - 1 ) ) + ( x ) ;
				int upleft = ( rows * ( y - 1 ) ) + ( x - 1 ) ;
				int upright = ( rows * ( y - 1 ) ) + ( x + 1 ) ;
				int down = ( rows * ( y + 1 ) ) + ( x ) ;
				int downleft = ( rows * ( y + 1 ) ) + ( x - 1 ) ;
				int downright = ( rows * ( y + 1 ) ) + ( x + 1 ) ;
				if ( u ) {
					if ( mines [ up ] ) {
						temp ++ ;
					}
					if ( l ) {
						if ( mines [ upleft ] ) {
							temp ++ ;
						}
					}
					if ( r ) {
						if ( mines [ upright ] ) {
							temp ++ ;
						}
					}
				}
				if ( d ) {
					if ( mines [ down ] ) {
						temp ++ ;
					}
					if ( l ) {
						if ( mines [ downleft ] ) {
							temp ++ ;
						}
					}
					if ( r ) {
						if ( mines [ downright ] ) {
							temp ++ ;
						}
					}
				}
				if ( l ) {
					if ( mines [ left ] ) {
						temp ++ ;
					}
				}
				if ( r ) {
					if ( mines [ right ] ) {
						temp ++ ;
					}
				}
				numbers [ cur ] = temp ;
			}
		}
	}

	public void gameSetup ( ) {
		firstMove = true ;
		for ( int x = 0 ; x < rows ; x ++ ) {
			for ( int y = 0 ; y < cols ; y ++ ) {
				mines [ ( rows * y ) + x ] = false ;
				clickdone [ ( rows * y ) + x ] = false ;
				clickable [ ( rows * y ) + x ] = true ;
				buttons [ ( rows * y ) + x ] = new JButton ( /*"" + ( x * y )*/) ;
				buttons [ ( rows * y ) + x ].setPreferredSize ( new Dimension (
						25 , 25 ) ) ;
				buttons [ ( rows * y ) + x ].addActionListener ( this ) ;
				buttons [ ( rows * y ) + x ].addMouseListener ( this ) ;
			}
		}
	}

	public void initializeBoard ( ) {
		if ( ! firstMove ) {
			fillMines ( ) ;
			fillNumbers ( ) ;
		}
	}
	public void resetBoard ( ) {
		firstMove = true ;
		for ( int x = 0 ; x < rows ; x ++ ) {
			for ( int y = 0 ; y < cols ; y ++ ) {
				mines [ ( rows * y ) + x ] = false ;
				clickdone [ ( rows * y ) + x ] = false ;
				clickable [ ( rows * y ) + x ] = true ;
				buttons [ ( rows * y ) + x ].setEnabled ( true ) ;
				buttons [ ( rows * y ) + x ].setIcon(null) ;
				buttons [ ( rows * y ) + x ].setText ( "" ) ;
			}
		}
		lost = false ;
		mineLabel.setText ( "mines: " + numMines + " marked: 0" ) ;
	}

	

	public Player()
	{
		gameLayout();
		localPlayEnabled = true;
	}
   public Player(String serverName, int serverPort)
   {  
	   addWindowListener(new WindowAdapter() {
		      public void windowClosing(WindowEvent e) {
		    	  try {
					streamOut.writeUTF(".bye");
					streamOut.flush();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
		          System.exit(0);
		        }
		      });
	   System.out.println("Establishing connection. Please wait ...");
      try
      {  
    	 socket = new Socket(serverName, serverPort);
         System.out.println("Connected: " + socket);
         start();
      }
      catch(UnknownHostException uhe)
      {  
    	  System.out.println("Host unknown: " + uhe.getMessage()); 
    	  handle_network_close();
      }
      catch(IOException ioe)
      {  
    	  System.out.println("Unexpected exception: " + ioe.getMessage()); 
    	  handle_network_close();
      }
   }
   //function that sends commands to the server
   public void run()
   {  while (thread != null)
      {  try
         {  streamOut.writeUTF(console.readLine());
            streamOut.flush();
         }
         catch(IOException ioe)
         {  System.out.println("Sending error: " + ioe.getMessage());
            stop();
            handle_network_close();
         }
      }
   }
   public void handle(String msg)
   {  if (msg.equals(".bye"))
      {  
	     System.out.println("Good bye. Press RETURN to exit ...");
      	 this.dispose();
         stop();
         handle_network_close();
      }
   else if(msg.equals("server.start"))
   {
	   my_turn = true;
	   JOptionPane.showMessageDialog ( null ,
				"You move first" , "Server Message" ,
				JOptionPane.ERROR_MESSAGE ) ;
 	  gameLayout ( ) ;
   }
   else if(msg.equals("server.second"))
   {
	   my_turn = false;
	   JOptionPane.showMessageDialog ( null ,
				"You move second...wait for your turn" , "Server Message" ,
				JOptionPane.ERROR_MESSAGE ) ;
 	  gameLayout ( ) ;
   }
   else if(msg.endsWith(".doLose")){
	   doLose();
   }
   else if(msg.endsWith(".doWin")){
	   doWin();
   }
      else
         System.out.println(msg);
   }
   public void start() throws IOException
   {  console   = new DataInputStream(System.in);
      streamOut = new DataOutputStream(socket.getOutputStream());
      if (thread == null)
      {  
    	  client = new PlayerThread(this, socket);
          thread = new Thread(this);                   
          thread.start();
      }
   }
   public void stop()
   {  
	   if (thread != null){  
		   thread.stop();  
		   thread = null;
      }
      try
      {  if (console   != null)  console.close();
         if (streamOut != null)  streamOut.close();
         if (socket    != null)  socket.close();
      }
      catch(IOException ioe)
      {  
    	  System.out.println("Error closing ...");  
    	  handle_network_close();
      }
      client.close();  
      client.stop();
      handle_network_close();
   }
   //public static void main(String args[])
   //{  Player client = null;
    //  if (args.length != 2){
     //    System.out.println("Usage: java ChatClient host port...using default");
      //	 client = new Player("127.0.0.1", 4444);
     // }
     // else
      //   client = new Player(args[0], Integer.parseInt(args[1]));
  // }
   
   public void doCheck ( int x , int y ) {
		int cur = ( rows * y ) + x ;
		boolean l = ( x - 1 ) >= 0 ;
		boolean r = ( x + 1 ) < rows ;
		boolean u = ( y - 1 ) >= 0 ;
		boolean d = ( y + 1 ) < cols ;
		int left = ( rows * ( y ) ) + ( x - 1 ) ;
		int right = ( rows * ( y ) ) + ( x + 1 ) ;
		int up = ( rows * ( y - 1 ) ) + ( x ) ;
		int upleft = ( rows * ( y - 1 ) ) + ( x - 1 ) ;
		int upright = ( rows * ( y - 1 ) ) + ( x + 1 ) ;
		int down = ( rows * ( y + 1 ) ) + ( x ) ;
		int downleft = ( rows * ( y + 1 ) ) + ( x - 1 ) ;
		int downright = ( rows * ( y + 1 ) ) + ( x + 1 ) ;

		clickdone [ cur ] = true ;
		buttons [ cur ].setEnabled ( false ) ;
		if ( numbers [ cur ] == 0 && ! mines [ cur ] && ! lost && ! won ) {
			if ( u && ! won ) {
				if ( ! clickdone [ up ] && ! mines [ up ] ) {
					clickdone [ up ] = true ;
					buttons [ up ].doClick ( ) ;
				}
				if ( l && ! won ) {
					if ( ! clickdone [ upleft ] && numbers [ upleft ] != 0
							&& ! mines [ upleft ] ) {
						clickdone [ upleft ] = true ;
						buttons [ upleft ].doClick ( ) ;
					}
				}
				if ( r && ! won ) {
					if ( ! clickdone [ upright ] && numbers [ upright ] != 0
							&& ! mines [ upright ] ) {
						clickdone [ upright ] = true ;
						buttons [ upright ].doClick ( ) ;
					}
				}
			}
			if ( d && ! won ) {
				if ( ! clickdone [ down ] && ! mines [ down ] ) {
					clickdone [ down ] = true ;
					buttons [ down ].doClick ( ) ;
				}
				if ( l && ! won ) {
					if ( ! clickdone [ downleft ] && numbers [ downleft ] != 0
							&& ! mines [ downleft ] ) {
						clickdone [ downleft ] = true ;
						buttons [ downleft ].doClick ( ) ;
					}
				}
				if ( r && ! won ) {
					if ( ! clickdone [ downright ]
							&& numbers [ downright ] != 0
							&& ! mines [ downright ] ) {
						clickdone [ downright ] = true ;
						buttons [ downright ].doClick ( ) ;
					}
				}
			}
			if ( l && ! won ) {
				if ( ! clickdone [ left ] && ! mines [ left ] ) {
					clickdone [ left ] = true ;
					buttons [ left ].doClick ( ) ;
				}
			}
			if ( r && ! won ) {
				if ( ! clickdone [ right ] && ! mines [ right ] ) {
					clickdone [ right ] = true ;
					buttons [ right ].doClick ( ) ;
				}
			}
		} else {
			buttons [ cur ].setText ( "" + numbers [ cur ] ) ;
			if ( ! mines [ cur ] && numbers [ cur ] == 0 ) {
				buttons [ cur ].setText ( "" ) ;
			}
		}
		if ( mines [ cur ] && ! won ) {
			clickdone [ cur ] = false ;
			clickable [ cur ] = true ;
			buttons [ cur ].setEnabled ( true ) ;
			buttons [ cur ].setIcon ( null ) ;
			buttons [ cur ].setText ( "" ) ;
			buttons [ cur ].setIcon ( newGameOverMine ) ;
			clickdone [ cur ] = true ;
			doLose ( ) ;
		}
	}

	public void checkWin ( ) {
		for ( int x = 0 ; x < rows ; x ++ ) {
			for ( int y = 0 ; y < cols ; y ++ ) {
				int cur = ( rows * y ) + x ;
				if ( ! clickdone [ cur ] ) {
					if ( mines [ cur ] ) {
						continue ;
					} else {
						return ;
					}
				}
			}
		}

		doWin ( ) ;
	}

	public void doWin ( ) {
		if ( ! lost && ! won ) {
			won = true ;
			JOptionPane.showMessageDialog ( null ,
					"You won!\nPress OK to return to the Main Menu" , "WINNER" ,
					JOptionPane.INFORMATION_MESSAGE ) ;
			if(!localPlayEnabled){
				try {
					streamOut.writeUTF(".doLose");
					streamOut.flush();
				} catch (IOException e) {
				// 	TODO Auto-generated catch block
					e.printStackTrace();
					handle_network_close();
				}
			}
           new MineSweeper();
           this.dispose();
		}
	}

	public void doLose ( ) {
		if ( ! lost && ! won ) {
			lost = true ;
			for ( int i = 0 ; i < rows * cols ; i ++ ) {
				if ( ! clickdone [ i ] && mines [ i ] ) {
					buttons [ i ].setIcon( newMine ) ;
				}
				if ( ! clickdone [ i ] && ! mines [ i ] ) {
					buttons [ i ].doClick ( 0 ) ;
				}
			}
			JOptionPane.showMessageDialog ( null ,
					"Game Over!\nPress OK to return to the Main Menu" , "GAME OVER" ,
					JOptionPane.ERROR_MESSAGE ) ;
			if(!localPlayEnabled){
				try {
					streamOut.writeUTF(".doWin");
					streamOut.flush();
				} catch (IOException e) {
				// 	TODO Auto-generated catch block
					e.printStackTrace();
					handle_network_close();
				}
				}
			new MineSweeper();
	        this.dispose();
		}
	}
   
   public void actionPerformed ( ActionEvent e ) {
		if ( e.getSource ( ) == quit ) {
			menu.dispose ( ) ;
		}
		if ( ! won ) {
			for ( int x = 0 ; x < rows ; x ++ ) {
				for ( int y = 0 ; y < cols ; y ++ ) {
					if ( e.getSource ( ) == buttons [ ( rows * y ) + x ]
							&& ! won && clickable [ ( rows * y ) + x ] && firstMove ) {
						firstMove = false ;
						initializeBoard ( ) ;
						if ( mines [ ( rows * y ) + x ] ) {
							initializeBoard ( ) ;
						}
						doCheck ( x , y ) ;
						break ;
					}
					if ( e.getSource ( ) == buttons [ ( rows * y ) + x ]
							&& ! won && clickable [ ( rows * y ) + x ] && ! firstMove ) {
						doCheck ( x , y ) ;
						break ;
					}
				}
			}
		}
		if ( e.getSource ( ) == newGameButton ) {
			resetBoard ( ) ;
			won = false ;
			return ;

		}
		checkWin ( ) ;
	}
public void mouseClicked(MouseEvent arg0) {
	// TODO Auto-generated method stub
	
}
public void mouseEntered(MouseEvent arg0) {
	// TODO Auto-generated method stub
	
}
public void mouseExited(MouseEvent arg0) {
	// TODO Auto-generated method stub
	
}
public void mousePressed ( MouseEvent e ) {
	if ( e.getButton ( ) == 3 ) {
		int n = 0 ;
		for ( int x = 0 ; x < rows ; x ++ ) {
			for ( int y = 0 ; y < cols ; y ++ ) {
				if ( e.getSource ( ) == buttons [ ( rows * y ) + x ] ) {
					clickable [ ( rows * y ) + x ] = ! clickable [ ( rows * y )
							+ x ] ;
				}
				if ( ! clickdone [ ( rows * y ) + x ] ) {
					if ( ! clickable [ ( rows * y ) + x ] ) {
						buttons [ ( rows * y ) + x ].setIcon ( newFlag ) ;
						n ++ ;
					} else {
						buttons [ ( rows * y ) + x ].setIcon ( null ) ;
						buttons [ ( rows * y ) + x ].setText ( "" ) ;
					}
					mineLabel.setText ( "mines: " + numMines + " marked: "
							+ n ) ;
				}
			}
		}
	}
}
public void mouseReleased(MouseEvent arg0) {
	// TODO Auto-generated method stub
	
}
}